package preferences;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.io.*;

import javax.swing.text.StyleConstants;

import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.*;

import controller.Controller;

// Uniquement pour les valeurs par défaut de coloration
import Highlight.CodeDocument;

/**
 * Enregistrer et lire les préférences sauvegardées dans le fichier de préférences 
 * 
 * @author			Christos Kornaros, Grégory Moinat, Nicolas Muller
 * @version			1.0, 23.04.2008
 * Fichier :		Preferences.java
 * Remarque(s) :	Nécessite la librairie JDOM (http://www.jdom.org)
 * Compilateur : 	jdk1.6.0_02
*/ 
public class Preferences {
	
	private Controller controller;
	
	// Valeurs par défaut
	private final Integer defaultWindowPosX = 20;
	private final Integer defaultWindowPosY = 20;
	private final Integer defaultWindowSizeWidth = 300;
	private final Integer defaultWindowSizeHeight = 500;
	private final String actualPrefsVersion = "0.3";
	
	// Arborescence du fichier de préférences
	private String prefsPath;
	private Element codePrefs = new Element("codePrefs");
	private Attribute prefsVersion = new Attribute("prefsVersion", actualPrefsVersion);
	
	// Interface graphique
	private Element guiPrefs = new Element("guiPrefs");
	private Element windowPosition = new Element("windowPosition");
	private Attribute windowPositionX = new Attribute ("X", defaultWindowPosX.toString());
	private Attribute windowPositionY = new Attribute ("Y", defaultWindowPosY.toString());
	private Element windowSize = new Element("windowSize");
	private Attribute windowSizeWidth = new Attribute ("width", defaultWindowSizeWidth.toString());
	private Attribute windowSizeHeight = new Attribute ("height", defaultWindowSizeHeight.toString());
	private Document prefsDocument = new Document(codePrefs);
	
	// Préféfences pour la coloration
	private Element colorationPrefs = new Element("colorationPrefs");
	private Element keywords = new Element("keywords");
	private Attribute keywordsR = new Attribute("R", String.valueOf(CodeDocument.defaultKeywordColor.getRed()));
	private Attribute keywordsG = new Attribute("G", String.valueOf(CodeDocument.defaultKeywordColor.getGreen()));
	private Attribute keywordsB = new Attribute("B", String.valueOf(CodeDocument.defaultKeywordColor.getBlue()));
	private Element strings= new Element("strings");
	private Attribute stringsR = new Attribute("R", String.valueOf(CodeDocument.defaultStringColor.getRed()));
	private Attribute stringsG = new Attribute("G", String.valueOf(CodeDocument.defaultStringColor.getGreen()));
	private Attribute stringsB = new Attribute("B", String.valueOf(CodeDocument.defaultStringColor.getBlue()));
	private Element numbers = new Element("numbers");
	private Attribute numbersR = new Attribute("R", String.valueOf(CodeDocument.defaultNumberColor.getRed()));;
	private Attribute numbersG = new Attribute("G", String.valueOf(CodeDocument.defaultNumberColor.getGreen()));
	private Attribute numbersB = new Attribute("B", String.valueOf(CodeDocument.defaultNumberColor.getBlue()));
	private Element characters = new Element("characters");
	private Attribute charactersR = new Attribute("R", String.valueOf(CodeDocument.defaultCharacterColor.getRed()));
	private Attribute charactersG = new Attribute("G", String.valueOf(CodeDocument.defaultCharacterColor.getGreen()));
	private Attribute charactersB = new Attribute("B", String.valueOf(CodeDocument.defaultCharacterColor.getBlue()));
	private Element comments = new Element("comments");
	private Attribute commentsR = new Attribute("R", String.valueOf(CodeDocument.defaultCommentsColor.getRed()));
	private Attribute commentsG = new Attribute("G", String.valueOf(CodeDocument.defaultCommentsColor.getGreen()));
	private Attribute commentsB = new Attribute("B", String.valueOf(CodeDocument.defaultCommentsColor.getBlue()));
	private Element preprocess = new Element("preprocess");
	private Attribute preprocessR = new Attribute("R", String.valueOf(CodeDocument.defaultPreprocessColor.getRed()));
	private Attribute preprocessG = new Attribute("G", String.valueOf(CodeDocument.defaultPreprocessColor.getGreen()));
	private Attribute preprocessB = new Attribute("B", String.valueOf(CodeDocument.defaultPreprocessColor.getBlue()));
	
	// Préférences générales
	private Element generalPrefs = new Element("generalPrefs");
	private Attribute tabSize = new Attribute("tabSize", String.valueOf(CodeDocument.defaultTabSize));
	private Attribute replaceTabWithSpaces = new Attribute("replaceTabWithSpaces", String.valueOf(CodeDocument.defaultReplaceTab));

	

	/**
	 * Constructeur: Crée l'arborescence et éventuellement le fichier s'il n'existe pas encore
	 * @param controller Le controlleur de l'application afin d'accéder/modifier certains réglages
	 */
	public Preferences (Controller controller) {
		
		this.controller = controller;
		
		File prefs;
		
		// On détermine où va etre sauvé le fichier des préférences selon l'os
		if (System.getProperty("os.name").contains("Windows"))
		{
			new File(System.getProperty("user.home") + "\\CODE").mkdir();
			prefsPath = System.getProperty("user.home") + "\\CODE\\CODEPrefs.xml";
		}
		else if (System.getProperty("os.name").toLowerCase().startsWith("mac os x"))
			prefsPath = System.getProperty("user.home") + "/Library/Preferences/CODEPrefs.xml";
		else
		{
			new File(System.getProperty("user.home") + "/.CODE").mkdir();
			prefsPath = System.getProperty("user.home") + "/.CODE/.CODEPrefs.xml";
		}
				
		prefs = new File(prefsPath);
		// Mise en forme du fichier xml
		codePrefs.addContent(guiPrefs);
		codePrefs.addContent(colorationPrefs);
		codePrefs.setAttribute(prefsVersion);
		codePrefs.addContent(generalPrefs);
		
		// Interface graphique
		guiPrefs.addContent(windowPosition);
		guiPrefs.addContent(windowSize);
		// On crée les attributs
		windowPosition.setAttribute(windowPositionX);
		windowPosition.setAttribute(windowPositionY);
		windowSize.setAttribute(windowSizeWidth);
		windowSize.setAttribute(windowSizeHeight);
		
		// Coloration
		colorationPrefs.addContent(keywords);
		colorationPrefs.addContent(strings);
		colorationPrefs.addContent(numbers);
		colorationPrefs.addContent(characters);
		colorationPrefs.addContent(comments);
		colorationPrefs.addContent(preprocess);
		// On crée les attributs
		keywords.setAttribute(keywordsR);
		keywords.setAttribute(keywordsG);
		keywords.setAttribute(keywordsB);
		strings.setAttribute(stringsR);
		strings.setAttribute(stringsG);
		strings.setAttribute(stringsB);
		numbers.setAttribute(numbersR);
		numbers.setAttribute(numbersG);
		numbers.setAttribute(numbersB);
		characters.setAttribute(charactersR);
		characters.setAttribute(charactersG);
		characters.setAttribute(charactersB);
		comments.setAttribute(commentsR);
		comments.setAttribute(commentsG);
		comments.setAttribute(commentsB);
		preprocess.setAttribute(preprocessR);
		preprocess.setAttribute(preprocessG);
		preprocess.setAttribute(preprocessB);
		
		// Général
		generalPrefs.setAttribute(tabSize);
		generalPrefs.setAttribute(replaceTabWithSpaces);
		
		// Création du fichier s'il n'existe pas
		if (prefs.exists() == false)
			savePrefs();
		// On vérifie la version des préférences
		else
			try {
				
				// On crée un document de préférences provisoire
				// afin de ne pas mélanger les éventuels attributs manquant
				// avec le fichier de base
				Document testDocument = new Document();
				Element testRoot = new Element("codePrefs");;
				SAXBuilder sxb = new SAXBuilder();
				
				try {
					testDocument = sxb.build(new File(prefsPath));
				}
				catch (Exception e) {}
				
				testRoot = testDocument.getRootElement();	
				
				// Si la version des préférences n'est pas la bonne => nouveau fichier préférences
				if (!testRoot.getAttributeValue("prefsVersion").equals(actualPrefsVersion)) {
					prefs.delete();
					savePrefs();
				}
			}
			// En cas d'exception => nouveau fichier préférences
			catch (Exception e) {
				prefs.delete();
				savePrefs();
			}
	}


	/**
	 * Ecris dans le fichier les préférences dans leur état actuel
	 */
	private void savePrefs () {
		
		try
		{
			XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
			sortie.output(prefsDocument, new FileOutputStream(prefsPath));
		}
		catch (java.io.IOException e){e.printStackTrace();}
	}

	/**
	* Lit le fichier (attache la racine du document à l'objet codePrefs)
	* 
	*/ 
	private void readPrefs () {
		SAXBuilder sxb = new SAXBuilder();
		
		try {
			prefsDocument = sxb.build(new File(prefsPath));
		}
		catch (Exception e) {e.printStackTrace();}
		
		codePrefs = prefsDocument.getRootElement();	
	}

	/**
	* Sauve dans le fichier de préférence la position de la fenêtre
	*
	* @param x La position en x de la fenêtre
	* @param y La position en y de la fenêtre 
	*/  
	// Enregistrer dans le fichier de préférences la position de la fenêtre
	public void setWindowPosition (Integer x, Integer y) {
		
		// On attache la racine au fichier
		readPrefs();
		
		// On modifie les attributs
		codePrefs.getChild("guiPrefs").getChild("windowPosition").getAttribute("X").setValue(x.toString());
		codePrefs.getChild("guiPrefs").getChild("windowPosition").getAttribute("Y").setValue(y.toString());
		
		// On sauve
		savePrefs();
	}

	/**
	* Obtient la position de la fenêtre enregistrée dans le fichier de préférence
	*
	* @return La postion de la fenêtre
	*/  
	public Point getWindowPosition () {
		String x;
		String y;
		
			// On attache la racine au fichier
			readPrefs();
			
			// On lit les attributs de la position
			x = codePrefs.getChild("guiPrefs").getChild("windowPosition").getAttributeValue("X");
			y = codePrefs.getChild("guiPrefs").getChild("windowPosition").getAttributeValue("Y");
			
		return new Point(Integer.valueOf(x), Integer.valueOf(y));
	}
	
	/**
	* Obtient la taille de la fenêtre enregistrée dans le fichier de préférence
	*
	* @return La taille de la fenêtre
	*/  
	public Dimension getWindowSize () {
		String width;
		String height;
		
		// On attache la racine au fichier
		readPrefs();
		
		// On lit les attributs de la taille
		width = codePrefs.getChild("guiPrefs").getChild("windowSize").getAttributeValue("width");
		height = codePrefs.getChild("guiPrefs").getChild("windowSize").getAttributeValue("height");
			
		return new Dimension(Integer.valueOf(width), Integer.valueOf(height));
	}
	
	/**
	* Sauve dans le fichier de préférences la taille de la fenêtre enregistrée
	*
	* @param width La largeur de la fenêtre
	* @param height La hauteur de la fenêtre 
	*/  
	// Enregistrer dans le fichier de préférences la position de la fenêtre
	public void setWindowSize (Integer width, Integer height) {
		
		// On attache la racine au fichier
		readPrefs();
		
		// On modifie les attributs
		codePrefs.getChild("guiPrefs").getChild("windowSize").getAttribute("width").setValue(width.toString());
		codePrefs.getChild("guiPrefs").getChild("windowSize").getAttribute("height").setValue(height.toString());
		
		// On sauve
		savePrefs();
	}
	
	/**
	* Applique la coloration sauvée dans le fichier de préférences
	*
	*/  
	public void readColoration () {

		Color keywords;
		Color strings;
		Color numbers;
		Color characters;
		Color comments;
		Color preprocess;
		
		Element colorationPrefs = codePrefs.getChild("colorationPrefs");
		
		keywords = new Color (
					Integer.valueOf(colorationPrefs.getChild("keywords").getAttributeValue("R")),
					Integer.valueOf(colorationPrefs.getChild("keywords").getAttributeValue("G")),
					Integer.valueOf(colorationPrefs.getChild("keywords").getAttributeValue("B")));
		
		strings = new Color (
				Integer.valueOf(colorationPrefs.getChild("strings").getAttributeValue("R")),
				Integer.valueOf(colorationPrefs.getChild("strings").getAttributeValue("G")),
				Integer.valueOf(colorationPrefs.getChild("strings").getAttributeValue("B")));
		
		numbers = new Color (
				Integer.valueOf(colorationPrefs.getChild("numbers").getAttributeValue("R")),
				Integer.valueOf(colorationPrefs.getChild("numbers").getAttributeValue("G")),
				Integer.valueOf(colorationPrefs.getChild("numbers").getAttributeValue("B")));
		
		characters = new Color (
				Integer.valueOf(colorationPrefs.getChild("characters").getAttributeValue("R")),
				Integer.valueOf(colorationPrefs.getChild("characters").getAttributeValue("G")),
				Integer.valueOf(colorationPrefs.getChild("characters").getAttributeValue("B")));
		
		comments = new Color (
				Integer.valueOf(colorationPrefs.getChild("comments").getAttributeValue("R")),
				Integer.valueOf(colorationPrefs.getChild("comments").getAttributeValue("G")),
				Integer.valueOf(colorationPrefs.getChild("comments").getAttributeValue("B")));
		
		preprocess = new Color (
				Integer.valueOf(colorationPrefs.getChild("preprocess").getAttributeValue("R")),
				Integer.valueOf(colorationPrefs.getChild("preprocess").getAttributeValue("G")),
				Integer.valueOf(colorationPrefs.getChild("preprocess").getAttributeValue("B")));
		
		StyleConstants.setForeground(controller.getKeywordAttributes(), keywords);
		StyleConstants.setForeground(controller.getStringAttributes(), strings);
		StyleConstants.setForeground(controller.getNumberAttributes(), numbers);
		StyleConstants.setForeground(controller.getCharacterAttributes(), characters);
		StyleConstants.setForeground(controller.getCommentsAttributes(), comments);
		StyleConstants.setForeground(controller.getPreprocessAttributes(), preprocess);
	}
	
	/**
	* Sauve dans le fichier de préférence les couleurs pour la coloration lexicale
	*
	*/ 
	public void saveColoration () {
		
		// On attache la racine au fichier
		readPrefs();
		
		// On modifie les attributs
		
		Element colorationPrefs = codePrefs.getChild("colorationPrefs");
		
		
		// Mots-clés
		colorationPrefs.getChild("keywords").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getKeywordAttributes()).getRed()));
		colorationPrefs.getChild("keywords").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getKeywordAttributes()).getGreen()));
		colorationPrefs.getChild("keywords").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getKeywordAttributes()).getBlue()));
		
		// Chaines de caractères
		colorationPrefs.getChild("strings").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getStringAttributes()).getRed()));
		colorationPrefs.getChild("strings").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getStringAttributes()).getGreen()));
		colorationPrefs.getChild("strings").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getStringAttributes()).getBlue()));
		
		// Nombres
		colorationPrefs.getChild("numbers").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getNumberAttributes()).getRed()));
		colorationPrefs.getChild("numbers").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getNumberAttributes()).getGreen()));
		colorationPrefs.getChild("numbers").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getNumberAttributes()).getBlue()));
		
		// Caractères
		colorationPrefs.getChild("characters").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCharacterAttributes()).getRed()));
		colorationPrefs.getChild("characters").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCharacterAttributes()).getGreen()));
		colorationPrefs.getChild("characters").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCharacterAttributes()).getBlue()));
		
		// Commentaires
		colorationPrefs.getChild("comments").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCommentsAttributes()).getRed()));
		colorationPrefs.getChild("comments").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCommentsAttributes()).getGreen()));
		colorationPrefs.getChild("comments").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getCommentsAttributes()).getBlue()));
		
		// Preprocesseur
		colorationPrefs.getChild("preprocess").getAttribute("R").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getPreprocessAttributes()).getRed()));
		colorationPrefs.getChild("preprocess").getAttribute("G").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getPreprocessAttributes()).getGreen()));
		colorationPrefs.getChild("preprocess").getAttribute("B").setValue(String.valueOf(
				StyleConstants.getForeground(controller.getPreprocessAttributes()).getBlue()));
		
		// On sauve
		savePrefs();
	}

	/**
	* Applique les préférences générales enregistrées dans le fichier de préférences
	*
	*/  
	public void readGeneral () {
		
		Element generalPrefs = codePrefs.getChild("generalPrefs");
		
		controller.setTabSize(Integer.valueOf(generalPrefs.getAttributeValue("tabSize")));
		controller.setReplaceTab(Boolean.parseBoolean(generalPrefs.getAttributeValue("replaceTabWithSpaces")));
	}
	
	/**
	* Sauve dans le fichier de préférence les couleurs pour la coloration lexicale
	*
	*/ 
	public void saveGeneral () {
		
		// On attache la racine au fichier
		readPrefs();
		
		Element generalPrefs = codePrefs.getChild("generalPrefs");
		
		// On modifie les attributs
		generalPrefs.getAttribute("tabSize").setValue(String.valueOf(controller.getTabSize()));
		generalPrefs.getAttribute("replaceTabWithSpaces").setValue(String.valueOf(controller.isReplaceTab()));
		
		// On sauve
		savePrefs();
	}
	
}
